﻿namespace OverStore.Sql.TableSchema
{
    using System;
    using System.Reflection;

    /// <summary>
    /// Contains set of static methods allows to create different types of <see cref="ColumnBase"/> inheritors.
    /// </summary>
    public static class ColumnFactory
    {
        /// <summary>
        /// Creates the primary key column with result of specified type.
        /// </summary>
        /// <param name="columnType">Type of the column.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="originalIdentifierName">Name of the original identifier.</param>
        /// <param name="isMutable"><c>true</c> if the primary key result is mutable.</param>
        /// <param name="isDatabaseGenerated"><c>true</c> if the primary key result is generated by database.</param>
        /// <param name="errorMessage">Error message for exception thrown if value for the column is invalid.</param>
        public static ColumnBase CreateKey(Type columnType, string columnName, string originalIdentifierName, bool isMutable, bool isDatabaseGenerated, string errorMessage)
        {
            if (Object.ReferenceEquals(columnType, null))
                throw new ArgumentNullException("columnType");
            if (String.IsNullOrEmpty(columnName))
                throw new ArgumentNullException("columnName");
            if (String.IsNullOrEmpty(originalIdentifierName))
                throw new ArgumentNullException("originalIdentifierName");
            var genericMethod = typeof(ColumnFactory).GetMethod("KeyCore", BindingFlags.NonPublic | BindingFlags.Static);
            var closedMethod = genericMethod.MakeGenericMethod(columnType);
            return (ColumnBase)closedMethod.Invoke(null, new object[] { columnName, originalIdentifierName, isMutable, isDatabaseGenerated, errorMessage });
        }

        /// <summary>
        /// Creates the column.
        /// </summary>
        /// <param name="columnType">Type of the column.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="allowNull"><c>true</c> if column allows null result, <c>false</c> otherwise.</param>
        /// <param name="errorMessage">Error message for exception thrown if value for the column is invalid.</param>
        public static ColumnBase CreateColumn(Type columnType, string columnName, bool allowNull, string errorMessage)
        {
            if (Object.ReferenceEquals(columnType, null))
                throw new ArgumentNullException("columnType");
            if (String.IsNullOrEmpty(columnName))
                throw new ArgumentNullException("columnName");
            var genericMethod = typeof(ColumnFactory).GetMethod("ColumnCore", BindingFlags.NonPublic | BindingFlags.Static);
            var closedMethod = genericMethod.MakeGenericMethod(columnType);
            return (ColumnBase)closedMethod.Invoke(null, new object[] { columnName, allowNull, errorMessage });
        }

        private static ColumnBase KeyCore<T>(string columnName, string originalIdentifierName, bool isMutable, bool isDbGenerated, string errorMessage)
        {
            if (String.IsNullOrEmpty(columnName))
                throw new ArgumentNullException("columnName");
            if (String.IsNullOrEmpty(originalIdentifierName))
                throw new ArgumentNullException("originalIdentifierName");
            return new Key<T>
            {
                Name = columnName,
                OriginalIdentifierName = originalIdentifierName,
                IsMutable = isMutable,
                IsDatabaseGenerated = isDbGenerated,
                SkipOnInsert = isDbGenerated,
                ErrorMessage = errorMessage
            };
        }

        private static ColumnBase ColumnCore<T>(string columnName, bool allowNull, string errorMessage)
        {
            return new Column<T>
            {
                Name = columnName,
                AllowNull = allowNull,
                ErrorMessage = errorMessage
            };
        }
    }
}
